#pragma once

#include<pthread.h>
#include<iostream>
#include<string>
#include<vector>
#include<unordered_map>
#include"DataBaseSvr.hpp"

using std::cout;
using std::endl;
using std::string;
using std::vector;
using std::unordered_map;

enum UserStatus
{
    OFFLINE,
    ONLINE
};


//个人用户相关信息
class UserInfo
{
    public:
        UserInfo(int userid, const string& nickname, const string& school,
                const string& telnum, const string& passwd)
        {
            userid_ = userid;
            nickname_ = nickname;
            school_ = school;
            telnum_ = telnum;
            passwd_ = passwd;
            user_status_ = OFFLINE;
            tcp_sock_ = -1;
            friend_id_.clear();
        }
        UserInfo()
        {}

        ~UserInfo()
        {}

    public:
        int userid_;
        string nickname_;
        string school_;
        string telnum_;
        string passwd_;

        //保存当前用户的状态信息（是否在线）枚举
        //OFFLINE/ONLINE
        int user_status_;
        //登录的客户端对应的套接字描述符
        int tcp_sock_;
        //用来保存这个用户的好友信息的数组
        vector<int> friend_id_;
};


class UserManager
{
    public:
        UserManager()
        {
            user_map_.clear();
            pthread_mutex_init(&map_lock_, NULL);
            prepare_id_ = -1;
            db_ = NULL;
        }
        ~UserManager()
        {
            pthread_mutex_destroy(&map_lock_);
            if(db_)
            {
                delete db_;
                db_ = NULL;
            }
        }

        /*
         *  初始化工作
         * */
        bool UserManagerInit()
        {
            //1、连接数据库
            db_ = new DataBaseSvr();
            if(NULL == db_)
            {
                printf("create db case failed!\n");
                return false;
            }
            if(db_->MysqlInit() == false)
            {
                return false;
            }

            //2、查询所有用户信息进行维护(调用数据库模块提供的GetAllUser接口)
            Json::Value all_user;
            if(false == db_->GetAllUser(&all_user))
            {
                printf("GetAllUser failed!\n");
                return false;
            }
            for(int i = 0; i < (int)all_user.size(); i++)
            {
                //Json中的operator[]不支持使用size_t类型的数据作为下标
                UserInfo ui;
                ui.nickname_ = all_user[i]["nickname"].asString();
                ui.passwd_ = all_user[i]["passwd"].asString();
                ui.school_ = all_user[i]["school"].asString();
                ui.telnum_ = all_user[i]["telnum"].asString();
                ui.userid_ = all_user[i]["userid"].asInt();

                ui.user_status_ = OFFLINE;

                //个人好友信息
                if(false == db_->GetFriend(ui.userid_, &ui.friend_id_))
                {
                    return false;
                }

                //将所有的用户信息保存在unordered_map容器中
                //注意加锁
                pthread_mutex_lock(&map_lock_);
                user_map_[ui.userid_] = ui;
                if(ui.userid_ > prepare_id_)
                {
                    prepare_id_ = ui.userid_ + 1;
                }
                pthread_mutex_unlock(&map_lock_);
            }

            return true;
        }

        /*
         * 处理注册请求
         *   userid:注册成功时以出参的方式将用户id告知调用者
         *   如果注册失败，该值为负数
         *   注册成功，函数返回0，否则返回-1
         * */
        int DealRegister(const string& nickname, const string& school, 
                         const string& telnum, const string& passwd, int* userid)
        {
            //判断注册信息是否合法
            if(nickname.empty() || school.empty() || telnum.empty() || passwd.empty())
            {
                printf("Register info error!\n");
                *userid = -2;
                return -1;
            }
            pthread_mutex_lock(&map_lock_);
            auto iter = user_map_.begin();
            while(iter != user_map_.end())
            {
                if(iter->second.telnum_ == telnum)
                {
                    printf("No duplicate registration!\n");
                    *userid = -2;
                    pthread_mutex_unlock(&map_lock_);
                    return -1;
                }
                ++iter;
            }
            //是一个合法的注册信息
            UserInfo ui(prepare_id_, nickname, school, telnum, passwd);
            *userid = prepare_id_;
            ++prepare_id_;
            user_map_[ui.userid_] = ui;
            pthread_mutex_unlock(&map_lock_);

            db_->InsertUser(ui.userid_, nickname, school, telnum, passwd);
            return 0;
        }

        /*
         * 处理登录请求
         * 登陆成功返回用户id,否则返回-1
         * */
        int DealLogin(const string& telnum, const string& passwd, int sockfd)
        {
            //判断登录信息是否合法
            if(telnum.empty() || passwd.empty())
            {
                printf("Login information cannot be empty!\n");
                return -1;
            }
            pthread_mutex_lock(&map_lock_);
            auto iter = user_map_.begin();
            while(iter != user_map_.end())
            {
                if(iter->second.telnum_ == telnum)
                {
                    break;
                }
                ++iter;
            }
            if(iter == user_map_.end())
            {
                printf("User does not exist!\n");
                pthread_mutex_unlock(&map_lock_);
                return -1;
            }
            //用户存在，核实密码
            if(passwd != iter->second.passwd_)
            {
                printf("Password is error!\n");
                pthread_mutex_unlock(&map_lock_);
                return -1;
            }
            //更改用户状态信息为ONLINE
            iter->second.user_status_ = ONLINE;
            int userid = iter->second.userid_;
            iter->second.tcp_sock_ = sockfd;
            pthread_mutex_unlock(&map_lock_);

            return userid;
        }

        /*
         * 判断用户的登陆状态
         * 用户合法返回登陆状态，否则返回-1
         * */
        int IsLogin(int userid)
        {
            pthread_mutex_lock(&map_lock_);
            auto iter = user_map_.find(userid);
            if(iter == user_map_.end())
            {
                printf("userid : %d does not exist!\n", userid);
                pthread_mutex_unlock(&map_lock_);
                return -1;
            }
            if(iter->second.user_status_ == OFFLINE)
            {
                pthread_mutex_unlock(&map_lock_);
                return OFFLINE;
            }
            pthread_mutex_unlock(&map_lock_);
            return ONLINE;
        }

        int IsLogin(const string& telnum, UserInfo* ui)
        {
            pthread_mutex_lock(&map_lock_);
            auto iter = user_map_.begin();
            while(iter != user_map_.end())
            {
                if(iter->second.telnum_ == telnum)
                {
                    break;
                }
                ++iter;
            }
            if(iter == user_map_.end())
            {
                pthread_mutex_unlock(&map_lock_);
                return -1;
            }
            *ui = iter->second;
            if(iter->second.user_status_ == OFFLINE)
            {
                pthread_mutex_unlock(&map_lock_);
                return OFFLINE;
            }
            pthread_mutex_unlock(&map_lock_);
            return ONLINE;
        }
        /*
         * 获取用户信息
         * ui:出参，成功获取到用户信息后由ui负责带出
         * */
        bool GetUserInfo(int userid, UserInfo* ui)
        {
            pthread_mutex_lock(&map_lock_);
            auto iter = user_map_.find(userid);
            if(iter == user_map_.end())
            {
                pthread_mutex_unlock(&map_lock_);
                return false;
            }

            *ui = iter->second;
            pthread_mutex_unlock(&map_lock_);
            return true;
        }
        /*
         * 获取好友信息
         * fri:出参，成功获取到userid对应的好友信息后，将其好友的id保存在fri中带出
         * */
        bool GetFriends(int userid, vector<int>* fri)
        {
            pthread_mutex_lock(&map_lock_);
            auto iter = user_map_.find(userid);
            if(iter == user_map_.end())
            {
                pthread_mutex_unlock(&map_lock_);
                return false;
            }
            *fri = iter->second.friend_id_;
            pthread_mutex_unlock(&map_lock_);
            return true;
        }

        /*
         * 添加好友
         * */
        void SetFriend(int userid1, int userid2)
        {
            pthread_mutex_lock(&map_lock_);
            auto iter1 = user_map_.find(userid1);
            auto iter2 = user_map_.find(userid2);
            if(iter1 == user_map_.end() || iter2 == user_map_.end())
            {
                pthread_mutex_unlock(&map_lock_);
                return;
            }
            //说明两个用户均是合法的
            iter1->second.friend_id_.push_back(userid2);
            iter2->second.friend_id_.push_back(userid1);
            pthread_mutex_unlock(&map_lock_);

            //更新数据库表
            db_->InsertFriend(userid1, userid2);
            db_->InsertFriend(userid2, userid1);
        }

        void SetUserOffLine(int sockfd)
        {
            pthread_mutex_lock(&map_lock_);
            auto iter = user_map_.begin();
            while(iter != user_map_.end())
            {
                if(iter->second.tcp_sock_ == sockfd)
                {
                    iter->second.user_status_ = OFFLINE;
                }
                iter++;
            }
            pthread_mutex_unlock(&map_lock_);

        }
    private:
        //保存k-v模型的数据，k:userid v：对应的用户信息
        unordered_map<int, UserInfo> user_map_;
        //STL的容器不是线程安全的，因此需要使用互斥锁保证线程安全
        pthread_mutex_t map_lock_;
        //针对注册用户分配的userid
        int prepare_id_;
        //指向数据库管理模块DataBaseSvr的指针，操作数据库
        DataBaseSvr* db_;
};
